/// \file		WnConnBase.cpp
/// \brief		WIND连接基类的定义文件
/// \author		xuefeng
/// \version	1.0
/// \date		2011-07-19
/// \history	2011-07-19 新建

#include "ComHeader.h"

#include "WnConnBase.h"
#include "WnUtility.h"
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>

#include <boost/algorithm/string.hpp>


// 超过10M的包认为是大包
const int MAX_PACKET_LEN = 10 * 1024 * 1024;

// 读写锁
typedef boost::shared_lock<boost::shared_mutex> readLock;
typedef boost:: unique_lock<boost::shared_mutex> writeLock;

WnConnBase::WnConnBase(END_POINT_APTR apEndPoint)
  : m_blConnected(false)
  , m_oConnectedMutex()
  , m_blWriting(false)
  , m_oWritingMutex()
  , m_oSendPacketList()
  , m_nCurrentListSize(0)
  , m_oLastestSendTime(second_clock::local_time())
  , m_nRecvBufferSize(RECV_BUFFER_SIZE)
  , m_pbRecvBuffer(new unsigned char[m_nRecvBufferSize])
  , m_tHeader()
  , m_nReadBytes(0)
  , m_pbBodyData(NULL)
  , m_nBodySize(0)
{
  {
    writeLock lock(m_mtx_apLocalEndPoint);
    m_apLocalEndPoint = apEndPoint;
  }

  // 生成sessionid. 算法如下：
  // 当前时间点(年月日时分秒) + 时序，组合成一个64位长度sessinid
  static int sn = 1;

  if(sn > 9999)
    sn = 1;

  // 获取当前时间点
  string tm = to_iso_string(second_clock::local_time());
  boost::erase_all(tm, "T"/*is_any_of("T")*/);
  stringstream ss;
  ss << tm << sn;
  m_client_id = ss.str();

  ++sn;

  // 之前使用boost库的uuid算法来生成
  //m_client_id(to_string(boost::uuids::random_generator()())) // uuid/clientid
}

WnConnBase::~WnConnBase(void)
{
  delete[] m_pbRecvBuffer;
  delete[] m_pbBodyData;
}

void WnConnBase::Close()
{
  SetConnectedState(false);

  {
    writeLock lock(m_mtx_apLocalEndPoint);
    m_apLocalEndPoint.reset();
  }

  OnClose();
}

void WnConnBase::SetConnectedState(bool blConnected)
{
  writeLock lock(m_oConnectedMutex);
  m_blConnected = blConnected;
}

bool WnConnBase::IsConnected()
{
  readLock lock(m_oConnectedMutex);
  return m_blConnected;
}

void WnConnBase::HandleInData(CONNECTION_APTR apConnection, size_t nLength)
{
  unsigned int nCopyLen = 0;
  unsigned int nRemainLen = 0;
  unsigned int nNeedLen = 0;
  unsigned int nCheckLen = 0;
  unsigned int nSTXLen = sizeof(STX);
  unsigned int nETXLen = sizeof(ETX);
  unsigned int nHeaderLen = sizeof(TAG_PACKET_HEADER);

  const unsigned char* pbPos = m_pbRecvBuffer;
  const unsigned char* pbEnd = m_pbRecvBuffer + nLength; // 当前包末尾

  do
  {
    // 查找STX
    if(m_nReadBytes == 0)
    {
      bool first = true;
      while(pbPos < pbEnd)
      {
        // 注意位置递增
        if(*(pbPos++) == STX)
        {
          m_nReadBytes = 1;
          break;
        }
        else
        {
          if(first)
          {
            stringstream ss;
            ss << "file:" << __FILE__ << ", function:" << __FUNCTION__ << ", line:" << __LINE__ << ". @@@. "
              << "数据包校验有误. info:" << "流体头";
            OnMsg(2, ss.str());
          }
        }

        first = false;
      }
    }

    // 到达数据尾部
    if(pbPos == pbEnd)
      break;

    // 检查数据包开始标志
    if(m_nReadBytes == 1)
    {
      // 注意位置递增
      if(*(pbPos++) == PACKET_START_FLAG)
      {
        m_nReadBytes = 2;
        m_tHeader.bStartFlag = PACKET_START_FLAG;
      }
      else
      {
        // 无标志，重新查找包头
        m_nReadBytes = 0;

        {
          stringstream ss;
          ss << "file:" << __FILE__ << ", function:" << __FUNCTION__ << ", line:" << __LINE__ << ". @@@. "
            << "数据包校验有误. info:" << "数据包头";
          OnMsg(2, ss.str());
        }

        continue;
      }
    }

    // 到达数据尾部
    if(pbPos == pbEnd)
      break;

    nCheckLen = nSTXLen + nHeaderLen;

    // 获取包头
    if(m_nReadBytes < nCheckLen)
    {
      nRemainLen = static_cast<unsigned int>(pbEnd - pbPos);
      nNeedLen = nCheckLen - m_nReadBytes;
      nCopyLen = (nRemainLen > nNeedLen) ?	nNeedLen : nRemainLen;

      memcpy(reinterpret_cast<unsigned char*>(&m_tHeader) + m_nReadBytes - nSTXLen, pbPos, nCopyLen);

      m_nReadBytes += nCopyLen;
      pbPos += nCopyLen;

      // 检查包头
      if(m_nReadBytes == nCheckLen)
      {
        if(m_tHeader.bHeaderLen != nHeaderLen)
        {
          // 重新查找包头
          m_nReadBytes = 0;

          {
            stringstream ss;
            ss << "file:" << __FILE__ << ", function:" << __FUNCTION__ << ", line:" << __LINE__ << ". @@@. "
              << "数据包校验有误. info:" << "数据包长度不符";
            OnMsg(2, ss.str());
          }

          // 通知数据错误
          if(m_apLocalEndPoint)
            m_apLocalEndPoint->OnDataError(apConnection);

          continue;
        }

        // 分配包体内存
        WnUtility::StreamToInteger(m_tHeader.bPacketLen, m_nBodySize);

        if(m_nBodySize > MAX_PACKET_LEN)
        {
          stringstream ss;
          ss << "file:" << __FILE__ << ", function:" << __FUNCTION__ << ", line:" << __LINE__ << ". @@@. "
            << "检测到大数据包. info:" << "数据包长度:" << m_nBodySize;
          OnMsg(2, ss.str());
        }

        m_pbBodyData = new unsigned char[m_nBodySize + nETXLen];
      }
    }

    // 到达数据尾部
    if(pbPos == pbEnd)
      break;

    // 完整包的总长度
    nCheckLen = nSTXLen + nHeaderLen + m_nBodySize + nETXLen;

    // 获取包体
    if(m_nReadBytes < nCheckLen)
    {
      nRemainLen = static_cast<unsigned int>(pbEnd - pbPos);
      nNeedLen = nCheckLen - m_nReadBytes;
      nCopyLen = (nRemainLen > nNeedLen) ? nNeedLen : nRemainLen;

      memcpy(m_pbBodyData + m_nReadBytes - nSTXLen - nHeaderLen, pbPos, nCopyLen);

      m_nReadBytes += nCopyLen;
      pbPos += nCopyLen;

      // 检查ETX （整个完整包读完后，校验包标记）
      if(m_nReadBytes == nCheckLen)
      {
        if(m_pbBodyData[m_nBodySize] != ETX) // 包尾不符，报数据有误的错误
        {
          // 重新查找包头
          m_nReadBytes = 0;

          // 通知数据错误
          if(m_apLocalEndPoint)
            m_apLocalEndPoint->OnDataError(apConnection);

          {
            stringstream ss;
            ss << "file:" << __FILE__ << ", function:" << __FUNCTION__ << ", line:" << __LINE__ << ". @@@. "
              << "数据包校验有误. info:" << "数据包尾";
            OnMsg(2, ss.str());
          }

          delete []m_pbBodyData;
          m_pbBodyData = NULL;
          continue;
        }

        // 通知数据
        if(m_apLocalEndPoint)
          m_apLocalEndPoint->OnReceiveData(apConnection, PACKET_APTR(new WnPacket(m_tHeader, m_pbBodyData, m_nBodySize)));

        // 复位。 读取至包尾，该数据包读取完毕
        memset(&m_tHeader, 0, nHeaderLen);
        delete[] m_pbBodyData;
        m_pbBodyData = NULL;
        m_nReadBytes = 0;
        m_nBodySize = 0;
      }
    }
  }
  while(pbPos < pbEnd);
}

std::string WnConnBase::clientID()
{
  return m_client_id;
}

void WnConnBase::OnMsg(int type, const std::string& msg)
{
  readLock lock(m_mtx_apLocalEndPoint);
  if(m_apLocalEndPoint)
    m_apLocalEndPoint->OnMsg(type, msg);
}
